home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / GLX / buffer / bufferglx.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  19.6 KB  |  629 lines

  1. /*
  2.  * Copyright (C) 1991, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  */
  5. /*
  6.  * bufferglx:  a GL-Xlib version of Yusuf Attarwala's Motif-based 
  7.  *             "bufferglm.c" demo program.   bufferglx implements a mixed 
  8.  *             model methodology for switching back-and-forth between
  9.  *             single and double -buffer drawing modes.
  10.  * 
  11.  *       left mouse  :  switch to single buffer
  12.  *       middle mouse:  switch to double buffer
  13.  *       right mouse :  animate the green sphere in the current buffer mode
  14.  *
  15.  *       the program handles redraw (ConfigureNotify--move and resize) and
  16.  *       expose (pop, expose, de-iconify) and button (i.e. mouse) events.
  17.  *       ESC key exits the program.
  18.  *
  19.  *       Note the places where the XMapWindow/XUnmapWindow and Winset (a 
  20.  *       wrapper to GLXwinset) routines are implemented.  
  21.  *
  22.  *                                      ratmandu -- November, 1991   
  23.  */
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <X11/Xlib.h>
  27. #include <X11/Xutil.h>
  28. #include <X11/keysym.h>
  29. #include <X11/Xos.h>
  30. #include <X11/Xatom.h>
  31. #include <X11/extensions/XI.h>
  32. #include <X11/extensions/XInput.h>
  33. #include <gl/glws.h>
  34. #include <gl.h>
  35. #include <gl/sphere.h>
  36.  
  37. #define top     0
  38. #define SBglwin 1
  39. #define DBglwin 2
  40. #define WINMAX  3
  41.  
  42. Display *dpy;                                    /* The X server connection */
  43. Atom del_atom;
  44. Window wins[WINMAX];
  45.  
  46.  
  47. /* function declarations */
  48.  
  49. static void openwindow(char *);
  50. void Winset(Window, int);
  51. static Window glx_create_window(Window, int, int, int, int, int, char*);
  52. static void resize_buffer(long);
  53. static void clean_exit();
  54. void initGL();
  55. void bindLight();
  56. void setMatrix();
  57. void loop();
  58. void drawScene();
  59.  
  60.  
  61. static float material1[] = {
  62.     DIFFUSE,  0.6,0.2,0.2,
  63.     SPECULAR, 1.0,1.0,1.0,
  64.     SHININESS, 120.0,
  65.     LMNULL
  66. };
  67.  
  68. static float material2[] = {
  69.     DIFFUSE,  0.2,0.8,0.2,
  70.     SPECULAR, 1.0,1.0,1.0,
  71.     SHININESS, 120.0,
  72.     LMNULL
  73. };
  74.  
  75. static float infinite[] = {
  76.     AMBIENT,      0.3,0.3,0.3,
  77.     LOCALVIEWER,  0.0,
  78.     LMNULL
  79. };
  80.  
  81. static float white_light[] = {
  82.     AMBIENT,  0.2, 0.2, 0.2,
  83.     LCOLOR,   0.9, 0.9, 0.9,
  84.     POSITION, 10.0,50.0,50.0,0.0,
  85.     LMNULL
  86. };
  87.  
  88. static Matrix iden = {1.0, 0.0, 0.0, 0.0,
  89.                       0.0, 1.0, 0.0, 0.0,
  90.                       0.0, 0.0, 1.0, 0.0,
  91.                       0.0, 0.0, 0.0, 1.0 };
  92.  
  93. short ax, ay, az;     /* angles for the "twirling" green sphere to ride on */
  94. long xsize, ysize;                       /* current size-of-window keepers */
  95. long zfar;                /* used in czclear for the machine's zbuffer max */
  96. long buffermode;          /* flag tracks current window (single or double) */
  97.  
  98.  
  99.  
  100. main(argc,argv)
  101. int argc;
  102. char **argv;
  103. {
  104.     int myExpose, myConfigure, 
  105.     myButtRelease, myKeyPress;             /* store which events occur */
  106.  
  107.  
  108.  
  109.     myExpose = myConfigure = myButtRelease = myKeyPress = FALSE;
  110.  
  111.     openwindow(argv[0]);
  112.    
  113.    /* initialize *both* windows just as we wud do with winopen/winset */
  114.     Winset(wins[DBglwin], DBglwin);
  115.     initGL();                                          /* do GL init stuff */
  116.     setMatrix();
  117.     bindLight();
  118.   
  119.    /* start out making the singlebuffer window be our current GL window */
  120.     Winset(wins[SBglwin], SBglwin);
  121.     initGL();                                          /* do GL init stuff */
  122.     setMatrix();
  123.     bindLight();
  124.     drawScene();    /* and begin by drawing the scene in singlebuffer mode */
  125.  
  126.  
  127.     /*
  128.      * The event loop.
  129.      */
  130.     while (1) {         /* standard logic:  get event(s), process event(s) */
  131.  
  132.         XEvent event;
  133.         KeySym keysym;
  134.         char buf[4];
  135.  
  136.         gflush();                        /* For proper DGL performance */
  137.  
  138.     /* this "do while" loop does the `get events' half of the "get events,
  139.      *  process events" action of the infinite while.  this is to ensure
  140.      *  the event queue is always drained before the events that have come
  141.      *  in are processed.
  142.      */
  143.         do {
  144.  
  145.             XNextEvent(dpy, &event);
  146.             printf("event %d\n", event.type);
  147.             switch (event.type) {
  148.  
  149.  
  150.             /* "Expose" events are sort of like "REDRAW" in gl-speak in
  151.              *  terms of when a window becomes visible, or a previously
  152.              *  invisible part becomes visible.
  153.              */
  154.                 case Expose:                        /* Exposures */
  155.                     myExpose = TRUE;
  156.                     break;
  157.  
  158.  
  159.             /* "ConfigNotify" events are like "REDRAW" in terms of changes
  160.              *   to a window's size or position.
  161.              */
  162.                 case ConfigureNotify:                /* Resize GL manually */
  163.                     if (event.xconfigure.window == wins[top]) {
  164.             /* save the changed width/height of the parent X window */
  165.                         xsize = event.xconfigure.width;
  166.                         ysize = event.xconfigure.height;
  167.                         myConfigure = TRUE;
  168.                     } 
  169.                     break;
  170.  
  171.  
  172.             /* Wait for "ButtonRelease" events so the queue doesn't fill up
  173.          *  the way it wud if the user sits on ButtonPresss.
  174.          */
  175.                 case ButtonRelease:                        
  176.                     if (event.xbutton.button == Button1) {  /* LEFTMOUSE:  */
  177.                         buffermode = SBglwin;               /* switch to   */
  178.                         myButtRelease = TRUE;               /* singlebuffer*/
  179.                     } else if (event.xbutton.button == Button2) {
  180.                         buffermode = DBglwin;      /* MIDDLEMOUSE:  switch */
  181.                         myButtRelease = TRUE;      /* to doublebuffer mode */
  182.                     } else if (event.xbutton.button == Button3) {
  183.                         loop();                            /* RIGHTMOUSE:  */
  184.                     }                            /* twirl the green sphere */
  185.                     break;
  186.  
  187.  
  188.             /* "ClientMessage" is generated if the WM itself is being
  189.              *  gunned down and sends an exit signal to any running prog.
  190.              */
  191.                 case ClientMessage:
  192.                     if (event.xclient.data.l[0] == del_atom)
  193.                         clean_exit();
  194.                     break;
  195.  
  196.  
  197.             /* "KeyPress" events are those that would be generated before
  198.              *   whenever queueing up any KEYBD key via qdevice.
  199.              */
  200.                 case KeyPress:
  201.                    /* save out which unmodified key (i.e. the  key was
  202.                     *  not modified w/something like "Shift", "Ctrl",
  203.                     *  or "Alt") got pressed for use below.
  204.                     */
  205.                     XLookupString((XKeyEvent *)&event, buf, 4, &keysym, 0);
  206.                     myKeyPress = TRUE;
  207.                     break;
  208.  
  209.               }  /* end switch (event.type) */
  210.  
  211.  
  212.         } while (XPending(dpy));   /* end "do { } while".
  213.                                     * XPending() is like qtest()--it only
  214.                                     * tells you if there're any events
  215.                                     * presently in the queue.  it does not
  216.                                     * disturb queue's contents in any way.
  217.                                     */
  218.  
  219.     /* On an "Expose" event, redraw the affected pop'd or de-iconized window
  220.      */
  221.         if (myExpose) {
  222.             drawScene();                              /* draw the GL stuff */
  223.             myExpose = FALSE;               /* reset flag--queue now empty */
  224.         }
  225.  
  226.     /* On a "ConfigureNotify" event, the GL window has either been moved or
  227.      *  resized.  Respond accordingly and then redraw its contents.
  228.      */
  229.         if (myConfigure) {
  230.             if (buffermode == SBglwin) {
  231.         resize_buffer(DBglwin);   /* update viewport & CTM stuff */
  232.         resize_buffer(SBglwin);   /* for *both* windows and then */
  233.         bindLight();              /* bind the light to current 1 */
  234.             } else if (buffermode == DBglwin) {
  235.         resize_buffer(SBglwin);
  236.         resize_buffer(DBglwin);
  237.         bindLight();
  238.             } 
  239.         drawScene();
  240.         myConfigure = FALSE;            /* reset flag--queue now empty */
  241.         }
  242.  
  243.     /* On a "ButtonRelease" event, the flag gets set only for the LEFTMORE
  244.      *  or MIDDLEMOUSE.  we need to change the buffermode state in this case.
  245.      *  In either case, XStoreName updates the window's title bar, Winset
  246.      *  then switches to the current buffermode graphics context, and we
  247.      *  Map the current window *BEFORE* we unmap the previous one.  this
  248.      *  alleviates a painful "flicker" that occurs if you unmap the previous
  249.      *  window first.
  250.      */
  251.         if (myButtRelease) {
  252.             if (buffermode == SBglwin) {
  253.                 XStoreName(dpy,wins[top],"single[LEFTMOUSE]-buffer GL window");
  254.                 Winset(wins[SBglwin], SBglwin);
  255.         XMapWindow(dpy, wins[SBglwin]);
  256.         XUnmapWindow(dpy, wins[DBglwin]);
  257.             } else if (buffermode == DBglwin) {
  258.                 XStoreName(dpy,wins[top],"double[RIGHTMOUSE]-buffer GL window");
  259.                 Winset(wins[DBglwin], DBglwin);
  260.         XMapWindow(dpy, wins[DBglwin]);
  261.         XUnmapWindow(dpy, wins[SBglwin]);
  262.             }
  263.             myButtRelease = FALSE;
  264.         }
  265.  
  266.         /* On a keypress of Esc key, exit program.
  267.          */
  268.         if (myKeyPress) {
  269.             if (keysym == XK_Escape)
  270.                 clean_exit();
  271.         }
  272.  
  273.     }      /* end while(1) */
  274.  
  275. }
  276.  
  277.  
  278.  
  279. /*  openwindow -
  280.  *     establish connection to X server, get screen info, specify the
  281.  *     attributes we want the WM to try to provide, and create the 2
  282.  *     GL windows--one single and one double -buffered--that we'll employ.
  283.  */
  284. static void openwindow(char *progname) {
  285.  
  286.     int scrnnum;                               /* X screen number            */
  287.     int xorig, yorig;                          /* window (upper-left) origin */
  288.     long scrnheight;
  289.     XSizeHints Winhints;                          /* used to fix window size */
  290.     XSetWindowAttributes swa;
  291.     XColor gray;
  292.  
  293.  
  294.  
  295.    /* Connect to the X server and get screen info */
  296.     if ((dpy = XOpenDisplay(NULL)) == NULL) {
  297.         fprintf(stderr, "%s: cannot connect to X server %s\n",
  298.                                  progname, XDisplayName(NULL));
  299.         exit(1);
  300.     }
  301.     scrnnum = DefaultScreen(dpy);
  302.     scrnheight = DisplayHeight(dpy, scrnnum);
  303.  
  304.    /* define window initial size */
  305.     xorig = 50;  yorig = 50;
  306.     xsize = 300; ysize = 300;
  307.  
  308.    /* first create the top level X window.  */
  309.     wins[top] = XCreateSimpleWindow(dpy, RootWindow(dpy, scrnnum),
  310.                                        xorig, yorig, xsize, ysize, 0, 0, 0);
  311.     if (!(wins[top])) {
  312.         fprintf(stderr,"%s: couldn't create \"parent\" X window\n",progname);
  313.         exit(1);
  314.     }
  315.  
  316.    /* define string that will show up in the window title bar (and icon) */
  317.     XStoreName(dpy,wins[top],"single/double -buffer program");
  318.  
  319.    /* specify the values for the Window Size Hints we want to enforce:  this
  320.     *  window's aspect ratio needs to stay at 1:1, constrain min and max
  321.     *  window size, and specify the initial size of the window.
  322.     */
  323.     Winhints.width  = xsize;          /* specify desired x/y size of window */
  324.     Winhints.height = ysize;
  325.     Winhints.min_width = xsize;                       /* define min and max */
  326.     Winhints.max_width = scrnheight-1;                /* width and height   */
  327.     Winhints.min_height = ysize;
  328.     Winhints.max_height = scrnheight-1;
  329.     Winhints.min_aspect.x = 1;                /* keep aspect at a 1:1 ratio */
  330.     Winhints.max_aspect.x = 1;
  331.     Winhints.min_aspect.y = 1;
  332.     Winhints.max_aspect.y = 1;
  333.                                              /* set the corresponding flags */
  334.     Winhints.flags = USSize|PMaxSize|PMinSize|PAspect;
  335.     XSetNormalHints(dpy, wins[top], &Winhints);
  336.  
  337.  
  338.    /* create a singlebuffer GL imaging window */
  339.     if ((wins[SBglwin] = glx_create_window(wins[top], 0, 0, xsize, ysize,
  340.                                               SBglwin, progname)) == NULL) {
  341.         fprintf(stderr,"%s: couldn't create singlebuffer GL window\n",progname);
  342.         exit(1);
  343.     }
  344.  
  345.    /* create a doublebuffer GL imaging window */
  346.     if ((wins[DBglwin] = glx_create_window(wins[top], 0, 0, xsize, ysize,
  347.                                               DBglwin, progname)) == NULL) {
  348.         fprintf(stderr,"%s: couldn't create doublebuffer GL window\n",progname);
  349.         exit(1);
  350.     }
  351.  
  352.     /*  express interest in certain events */
  353.     XSelectInput(dpy, wins[top], StructureNotifyMask | ButtonPressMask |
  354.                                         KeyPressMask | ButtonReleaseMask);
  355.     XSelectInput(dpy, wins[SBglwin], StructureNotifyMask|ExposureMask);
  356.     XSelectInput(dpy, wins[DBglwin], StructureNotifyMask|ExposureMask);
  357.  
  358.    /* express interest in WM killing this app */
  359.     if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
  360.         XSetWMProtocols(dpy, wins[top], &del_atom, 1);
  361.  
  362.    /* ensure the GL colormap is installed for this app */
  363.     XSetWMColormapWindows(dpy, wins[top], wins, WINMAX);
  364.  
  365.    /* map the singlebuffer window to start up with */
  366.     XMapWindow(dpy, wins[SBglwin]);
  367.     XMapWindow(dpy, wins[top]);
  368. }
  369.  
  370.  
  371. char *typeToName[] = {
  372.     "RGB single buffer",
  373.     "RGB double buffer",
  374. };
  375.  
  376.  
  377. /*  A little helper wrapper for GLXwinset.
  378.  *  It passes the global variable "dpy" which contains the display, and it
  379.  *  checks the return value.  This makes the call to begin GL drawing a
  380.  *  little simpler.  Building in such automatic error checking is always a
  381.  *  "smooth move" (*not* like the cancerously-mutant human with the big
  382.  *  proboscis who is plastered all over the place urging people to be
  383.  *  likewise smoothly cancerous and hardly cool...).
  384.  */
  385. void Winset(Window w, int type)
  386. {
  387.     int rv = GLXwinset(dpy, w);
  388.     if (rv < 0) {
  389.         fprintf(stderr, "GLXWinset returned %d\ for the %s window\n", 
  390.                                             rv,   typeToName[type-1]);
  391.         exit(-1);
  392.     }
  393. }
  394.  
  395. /* Dorky little helper function used to build up a GLXconfig array.
  396.  */
  397. static void set_entry (GLXconfig* ptr, int b, int m, int a)
  398. {
  399.     ptr->buffer = b;
  400.     ptr->mode = m;
  401.     ptr->arg = a;
  402. }
  403.  
  404.  
  405. /* glx_create_window -- Create a single or a double -buffered, colorindex 
  406.  *                      X window suitable for GL imaging.
  407.  *
  408.  * Parameters:   parent      - parent window for the GL imaging window
  409.  *               x, y        - window origin relative to parent
  410.  *               w, h        - window width and height
  411.  *               type        - single or double -buffer specifier
  412.  *               progname    - name of executable
  413.  */
  414. static Window glx_create_window(Window parent, int x, int y, int w, int h, 
  415.                                 int type, char *progname)
  416. {
  417.     GLXconfig params[50];
  418.     GLXconfig *next, *retconfig;
  419.     Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy));
  420.     XVisualInfo* vis;
  421.     XVisualInfo template;
  422.     XSetWindowAttributes cwa;
  423.     XWindowAttributes pwa;
  424.     int i, nret;
  425.     Window win;
  426.  
  427.  
  428.  
  429.    /* first build an array in "params" that describes for GLXgetconfig(3G)
  430.     * the type of GL drawing that will be done.
  431.     */
  432.     next = params;
  433.     switch (type) {
  434.         case SBglwin:
  435.             set_entry(next++, GLX_NORMAL, GLX_DOUBLE, FALSE);
  436.             set_entry(next++, GLX_NORMAL, GLX_RGB, TRUE);
  437.             set_entry(next++, GLX_NORMAL, GLX_ZSIZE, GLX_NOCONFIG);
  438.             break;
  439.         case DBglwin:
  440.             set_entry(next++, GLX_NORMAL, GLX_DOUBLE, TRUE);
  441.             set_entry(next++, GLX_NORMAL, GLX_RGB, TRUE);
  442.             set_entry(next++, GLX_NORMAL, GLX_ZSIZE, GLX_NOCONFIG);
  443.             break;
  444.     }
  445.     set_entry(next, 0, 0, 0); /* The input to GLXgetconfig is null terminated */
  446.  
  447.    /*
  448.     * Get configuration data for a window based on above parameters.
  449.     * First we have to find out which screen the parent window is on,
  450.     * then we can call GXLgetconfig()
  451.     */
  452.     XGetWindowAttributes(dpy, parent, &pwa);
  453.     retconfig = GLXgetconfig(dpy, XScreenNumberOfScreen(pwa.screen), params);
  454.     if (retconfig == 0) {
  455.         printf("Sorry, can't support %s type of windows\n", typeToName[type-1]);
  456.         exit(-1);
  457.     }
  458.     /*
  459.      * The GL sets its own X error handlers, which aren't as informative
  460.      * when errors happen.  Calling XSetErrorHandler(0) here will
  461.      * reset back to the default Xlib version.
  462.      */
  463.     XSetErrorHandler(0);
  464.  
  465.    /*
  466.     * Scan through config info, pulling info we need to create a window
  467.     * that supports the rendering mode.
  468.     */
  469.     for (next = retconfig; next->buffer; next++) {
  470.         unsigned long buffer = next->buffer;
  471.         unsigned long mode = next->mode;
  472.         unsigned long value = next->arg;
  473.         switch (mode) {
  474.             case GLX_COLORMAP:
  475.                 if (buffer == GLX_NORMAL) {
  476.                     cmap = value;
  477.                 }
  478.                 break;
  479.             case GLX_VISUAL:
  480.                 if (buffer == GLX_NORMAL) {
  481.                     template.visualid = value;
  482.                     template.screen = DefaultScreen(dpy);
  483.                     vis = XGetVisualInfo(dpy, VisualScreenMask|VisualIDMask,
  484.                                           &template, &nret);
  485.                 }
  486.                 break;
  487.         }
  488.     }
  489.  
  490.    /* Create the window */
  491.     cwa.colormap = cmap;
  492.     cwa.border_pixel = 0;
  493.     win = XCreateWindow(dpy,parent,x,y,w,h,0,vis->depth,
  494.                         InputOutput,vis->visual,CWColormap|CWBorderPixel,&cwa);
  495.    /*
  496.     * now rescan configuration info, find the window slot GLXgetconfig 
  497.     * provided and fill it in with the window we just created.
  498.     */
  499.     for (next = retconfig; next->buffer; next++) {
  500.         if ((next->buffer == GLX_NORMAL) && (next->mode == GLX_WINDOW)) {
  501.             next->arg = win;
  502.             break;
  503.         }
  504.     }
  505.  
  506.    /* link to the GL */
  507.     if ((i = GLXlink(dpy, retconfig)) < 0) {
  508.         fprintf(stderr, "GLXlink returned %d for the %s window\n", 
  509.                                            i,   typeToName[type-1]);
  510.         exit(-1);
  511.     }
  512.  
  513.     return win;
  514. }
  515.  
  516.  
  517.  
  518. /*  window has been moved or resized so update viewport & CTM stuff.
  519.  */
  520. static void resize_buffer(long wintype) {
  521.  
  522.     XMoveResizeWindow(dpy, wins[wintype], 0, 0, xsize, ysize);
  523.     XSync(dpy, False);                           /* Need before GL reshape */
  524.     Winset(wins[wintype],wintype);
  525.     viewport(0, (short) (xsize - 1), 0, (short) (ysize - 1));
  526.     setMatrix();
  527. }
  528.  
  529.  
  530.  
  531. /*  clean up before exiting
  532.  */
  533. static void clean_exit(void)
  534. {
  535.     XCloseDisplay(dpy);
  536.     exit(0);
  537. }
  538.  
  539.  
  540. /* setup all necessary GL initialzation parameters.
  541.  */
  542. void
  543. initGL()
  544. {
  545.     zbuffer(TRUE);
  546.  
  547.     lmdef(DEFMATERIAL,1,11,material1);
  548.     lmdef(DEFMATERIAL,2,11,material2);
  549.     lmdef(DEFLIGHT,   1,14,white_light);
  550.     lmdef(DEFLMODEL,  1,7,infinite);
  551.  
  552.     lsetdepth(getgdesc(GD_ZMIN),getgdesc(GD_ZMAX));
  553.     shademodel(GOURAUD);
  554.     sphmode(SPH_PRIM,SPH_MESH);
  555.     sphmode(SPH_DEPTH,8);
  556.     buffermode = SBglwin;
  557.     zfar = getgdesc(GD_ZMAX);
  558. }
  559.  
  560.  
  561. /* define our Current Transformation Matrix
  562.  */
  563. void
  564. setMatrix()
  565. {
  566.     mmode(MPROJECTION);
  567.     ortho(-10.0,10.0,-10.0,10.0,-10.0,10.0);
  568.     mmode(MVIEWING);
  569.     loadmatrix(iden);
  570.     lookat(0.0,0.0,3.0,0.0,0.0,0.0,0);
  571. }
  572.  
  573.  
  574. /* update the lighting model
  575.  */
  576. void
  577. bindLight()
  578. {
  579.     pushmatrix();
  580.         loadmatrix(iden);
  581.         lmbind(LIGHT0,1);
  582.         lmbind(LMODEL,1);
  583.     popmatrix();
  584. }
  585.  
  586.  
  587. /* draw our two spheres
  588.  */
  589. void
  590. drawScene()
  591. {
  592.     static float sp1[] = {0.2,0.2,0.2,3.0};
  593.     static float sp2[] = {3.4,3.4,0.2,4.0};
  594.  
  595.     czclear(BLACK, zfar);
  596.  
  597.     pushmatrix();
  598.         lmbind(MATERIAL,1);
  599.         sphdraw(sp1);
  600.         rotate(ax,'x');
  601.         rotate(ay,'y');
  602.         rotate(az,'z');
  603.         lmbind(MATERIAL,2);
  604.         sphdraw(sp2);
  605.     popmatrix();
  606.  
  607.     if (buffermode == DBglwin) 
  608.         swapbuffers();
  609. }
  610.  
  611.  
  612. /* perform one loop of twirling the green sphere
  613.  */
  614. void
  615. loop()
  616. {
  617.     register int i;
  618.  
  619.     for (i=0;i<50;i++) {
  620.         ax += 50;
  621.         ay -= 25;
  622.         az += 50;
  623.         if (ax >= 3600)  ax = 0;
  624.         if (ay <= -3600) ay = 0;
  625.         if (az >= 3600)  az = 0;
  626.         drawScene();
  627.     }
  628. }
  629.